package org.ndx.jsg.output.freemarker;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.ndx.jsg.FileInfos;
import org.ndx.jsg.RelativePathProvider;
import org.ndx.jsg.util.JsgException;

import freemarker.template.Template;

/**
 * Infos generated for a directory
 * @author ndx
 *
 */
public class DirectoryInfos {
	private static final Logger logger = Logger.getLogger(DirectoryInfos.class.getName());
	/**
	 * Source directory
	 */
	Directory directory;
	
	private SortedSet<FileWritingInfos> infos = new TreeSet<FileWritingInfos>();
	
	private List<FileWritingInfos> timeline = new LinkedList<FileWritingInfos>();

	private NameResolver resolver;

	public DirectoryInfos(Directory directory, NameResolver resolver) {
		this.directory = directory;
		this.resolver = resolver;
	}

	/**
	 * Put the given file infos with its associated data 
	 * @param infos
	 * @param template template full name
	 * @param outputName
	 */
	public void put(FileInfos fileInfos) {
		infos.add(new FileWritingInfos(fileInfos, resolver));
	}

	public Directory getDirectory() {
		return directory;
	}
	
	public String getPath() {
		return directory.getRelativePath();
	}

	/**
	 * Simplify code by creating a model from the available infos
	 * @return
	 */
	public Map getModel() {
		Map returned = new HashMap();
		returned.put("directory", directory);
		returned.put("infos", infos);
		return returned;
	}

	public void saveAll(TemplateProvider provider, String relativePath, TemplateApplier applier) {
		if(logger.isLoggable(Level.INFO)) {
			logger.info("writing infos for directory "+directory.getRelativePath());
		}
		updateTimeline();
		for(FileWritingInfos fw : infos) {
			saveFileTemplates(fw, provider, relativePath, applier);
		}
		saveDirectoryTemplates(provider, relativePath, applier);
	}

	private void updateTimeline() {
		timeline.clear();
		timeline.addAll(infos);
	}

	private void saveFileTemplates(FileWritingInfos fw, TemplateProvider provider, String relativePath, TemplateApplier applier) {
		applyFileTemplates(fw.getInfos(), getData(fw), provider, applier);
	}

	private void applyFileTemplates(FileInfos infos, Object templateDataRoot, TemplateProvider provider, TemplateApplier applier) {
		File output = null;
		try {
			logger.log(Level.FINE, "applying templates to "
					+ infos.getFile().getAbsolutePath());
			for (Template template : provider.getFileTemplates()) {
				output = applier.applyTemplate(templateDataRoot, template, infos.getRelativePath(), true);
				logger.log(Level.FINE, "generated "
						+ output.getAbsolutePath()+" from template named "+template.getName());
			}
		} catch (Exception e) {
			logger.log(Level.WARNING, "unable to write template from input file "
					+ infos.getFile().getAbsolutePath() + (output==null ? "" : " while trying to write " + output.getAbsolutePath()), e);
		}
	}

	public Object getData(FileWritingInfos fw) {
		Map root = getModel();
		try {
			root.put("doc", fw.getDocument());
			root.put("writingDate", fw.getWritingDate());
			int index = timeline.indexOf(fw);
			if(index>0) {
				root.put("previous", timeline.get(index-1));
			}
			if(index<timeline.size()-1) {
				root.put("next", timeline.get(index+1));
			}
		} catch (Exception e) {
			throw new JsgException(e);
		}
		return root;
	}

	private void saveDirectoryTemplates(TemplateProvider provider,
			String relativePath, TemplateApplier applier) {
		File output = null;
		Object model = getModel();
		try {
			logger.log(Level.FINE, "applying templates to "
					+ directory.getName());
			for (Template template : provider.getDirectoriesTemplates()) {
				output = applier.applyTemplate(model, template, relativePath, false);
				logger.log(Level.FINE, "generated "
						+ output.getAbsolutePath()+" from template named "+template.getName());
			}
		} catch (Exception e) {
			logger.log(Level.WARNING, "unable to write template from input file "
					+ directory.getName() + (output==null ? "" : " while trying to write " + output.getAbsolutePath()), e);
		}
	}
}